/**
 * 
 */
package com.gragra.sampling.nonproj.structures.projective.singleroot;
import java.util.BitSet;
import com.gragra.sampling.PopulationProbabilityAssigner;
import com.gragra.sampling.ThreadedUniformAccess;
import com.gragra.sampling.binary.ConvertSample;
import com.gragra.sampling.sequentialStructure.WordAndTagHolder;
/**
 * @author Christoph Teichmann
 * 10.10.2013
 */
public class MHMHSingleRootProjectiveDepdTree extends SingleRootProjectiveDepdTree
{
	/**
	 * 
	 */
	private static final int HEADSAMPLINGROUNDS	= 2;
	/**
	 * 
	 * @param wth
	 * @param threads
	 * @param cs
	 */
	public MHMHSingleRootProjectiveDepdTree(WordAndTagHolder wth, int threads,
			ConvertSample cs)
	{
		super(wth, threads, cs);
		this.candidates = new BitSet[threads];
		for(int i=0;i<threads;++i)
		{this.candidates[i] = new BitSet();}
	}
	@Override
	protected void resampleRoot(PopulationProbabilityAssigner pa,
			double annealing, int thread, ThreadedUniformAccess tua)
	{
		for(int h=0;h<HEADSAMPLINGROUNDS;++h)
		{
			int root = this.getCurrentRoot(thread);
			double oldProb = this.makeLogProbForThisConsideringAnnealing(pa, annealing, thread);
			BitSet cand = getHeadExchangeCandidates(root,thread,pa);
			int newRoot = -1;
			int choice = tua.nextInt(thread, 1, cand.cardinality());
			double correction = Math.log(cand.cardinality());
			for(int k=0;k<choice;++k)
			{newRoot = cand.nextSetBit(newRoot+1);}
			this.setNewHead(root, newRoot, thread);
			this.setNewHead(newRoot, getRootPosition(), thread);
			cand = getHeadExchangeCandidates(newRoot,thread,pa);
			correction -= Math.log(cand.cardinality());
			double newProb = this.makeLogProbForThisConsideringAnnealing(pa, annealing, thread);
			if(Math.log(tua.nextDouble(thread))>newProb-oldProb+correction)
			{
				this.setNewHead(newRoot, root, thread);
				this.setNewHead(root, getRootPosition(), thread);
			}
		}
	}
	/**
	 * 
	 */
	private final BitSet[] candidates;
	/**
	 * 
	 * @param thread
	 * @return
	 */
	protected BitSet getCandidatesHolder(int thread)
	{
		this.candidates[thread].clear();
		return this.candidates[thread];
	}
	/**
	 * 
	 * @param root
	 * @param thread
	 * @param pa
	 * @return
	 */
	private BitSet getHeadExchangeCandidates(int root, int thread,
			PopulationProbabilityAssigner pa)
	{
		BitSet daughters = this.getDaughters(root, thread);
		int left = daughters.nextSetBit(0);
		int right = daughters.previousSetBit(daughters.size());
		BitSet bs = this.getCandidatesHolder(thread);
		if(left < root)
		{bs.set(left);}
		if(right > root)
		{bs.set(right);}
		return bs;
	}
	@Override
	protected void resampleAtPosition(int pos,
			PopulationProbabilityAssigner pa, double annealing, int thread,
			ThreadedUniformAccess tua)
	{
		double oldProb = this.makeLogProbForThisConsideringAnnealing(pa, annealing, thread);
		int oldHead = this.getHeadPosition(pos, thread);
		this.detach(pos, thread);
		BitSet bs = this.makeAllHeadCandidates(pos, thread, pa);
		int choice = tua.nextInt(thread, 1, bs.cardinality());
		int newHead = -1;
		for(int i=0;i<choice;++i)
		{newHead = bs.nextSetBit(newHead+1);}
		this.attach(pos, newHead, thread);
		double newProb = this.makeLogProbForThisConsideringAnnealing(pa, annealing, thread);
		if(Math.log(tua.nextDouble(thread)) > newProb-oldProb)
		{this.setNewHead(pos, oldHead, thread);}
	}
}